/*
 * The contents of this file are subject to the Mozilla Public License Version
 * 1.1 (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 * http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * The Original Code is ST_EXT-S_UIMAST.
 *
 * The Initial Developer of the Original Code is
 * University of Rome, Tor Vergata
 * Portions created by the Initial Developer are Copyright (C) 2009
 * the Initial Developer. All Rights Reserved.
 */

package it.uniroma2.art.uimast.xpts;

import it.uniroma2.art.ontapi.ARTResource;
import it.uniroma2.art.uimast.impl.types.BooleanVariant;
import it.uniroma2.art.uimast.impl.types.ByteVariant;
import it.uniroma2.art.uimast.impl.types.DoubleVariant;
import it.uniroma2.art.uimast.impl.types.EnumeratedVariant;
import it.uniroma2.art.uimast.impl.types.FloatVariant;
import it.uniroma2.art.uimast.impl.types.IntVariant;
import it.uniroma2.art.uimast.impl.types.LongVariant;
import it.uniroma2.art.uimast.impl.types.ShortVariant;
import it.uniroma2.art.uimast.impl.types.StringVariant;

import org.apache.uima.cas.Type;
import org.apache.uima.cas.TypeSystem;
import org.apache.uima.resource.metadata.AllowedValue;
import org.apache.uima.util.TypeSystemUtil;

/**
 * A variable able to hold different types of values. Every subclass can hold a
 * specific type of values. Using the abstract class, the client can manipulate
 * in an uniform way different types of values. The client shouldn't use an object,
 * before he has initialized it with a value.
 * 
 * Note that the data type are represented in according to the XML Schema standard.
 * 
 * @author <a href="mailto:manuel.fiorelli@gmail.com">Manuel Fiorelli</a>
 * 
 */
public abstract class Variant implements IVariantVisitable, Cloneable {
	/**
	 * Tests if a string is a valid lexical representation for the underlying
	 * type.
	 * 
	 * @param lexicalForm
	 *            a string
	 * @return <code>true</code> iff <code>lexicalForm</code> is a valid lexical
	 *         representation
	 */
	public abstract boolean isLexicalRepresentation(String lexicalForm);

	/**
	 * Sets the value from a given lexical representation.
	 * 
	 * @param lexicalForm
	 *            a string supposed to be a lexical representation for the
	 *            underlying type
	 * @return <code>true</code> if the value was successfully changed
	 */
	public abstract boolean setValue(String lexicalForm);

	/**
	 * Tests if an ontology data type is compatible with the underlying type.
	 * 
	 * @param dataType
	 *            an ontology data type
	 * @return <code>true</code> iff the data type is compatible with the
	 *         underlying type
	 */
	public abstract boolean isCompatible(ARTResource dataType);

	/**
	 * Gets a lexical representation of the current value.
	 * 
	 * @return a lexical representation
	 */
	public abstract String getLexicalRepresentation();

	/**
	 * Gets the current value as string.
	 * 
	 * @return the string representation of the current value
	 */
	public abstract String getValueAsString();

	/**
	 * Creates a variant of the concrete subclass, able to hold the values of a
	 * type in the type system.
	 * 
	 * @param typeSystem
	 *            a type system
	 * @param elementType
	 *            a type
	 * @return a variant
	 * @throws Exception
	 */
	public static Variant create(TypeSystem typeSystem, Type elementType)
			throws Exception {
		String name = elementType.getName();
		if (name.equals("uima.cas.Boolean"))
			return new BooleanVariant();

		if (name.equals("uima.cas.Byte"))
			return new ByteVariant();

		if (name.equals("uima.cas.Short"))
			return new ShortVariant();

		if (name.equals("uima.cas.Int"))
			return new IntVariant();

		if (name.equals("uima.cas.Long"))
			return new LongVariant();

		if (name.equals("uima.cas.Float"))
			return new FloatVariant();

		if (name.equals("uima.cas.Double"))
			return new DoubleVariant();

		if (name.equals("uima.cas.String"))
			return new StringVariant();

		if (typeSystem.getParent(elementType).getName().equals(
				"uima.cas.String")) {
			AllowedValue allowedValues[] = TypeSystemUtil.type2TypeDescription(
					elementType, typeSystem).getAllowedValues();
			return new EnumeratedVariant(allowedValues);
		} else {
			throw new Exception("Unsupported element type");
		}
	}

	// the method clone inherits its document comment from Object

	@Override
	public Variant clone() {
		Variant self = null;

		try {
			self = (Variant) super.clone();
		} catch (CloneNotSupportedException e) {
			e.printStackTrace();
		}
		return self;
	}
}
